﻿// Decompiled with JetBrains decompiler
// Type: TaleWorlds.MountAndBlade.View.MissionViews.Order.OrderFlag
// Assembly: TaleWorlds.MountAndBlade.View, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
// MVID: 61152EB8-513B-4D4A-8AC0-0D1465C6CF6C
// Assembly location: D:\steam\steamapps\common\Mount & Blade II Bannerlord\Modules\Native\bin\Win64_Shipping_Client\TaleWorlds.MountAndBlade.View.dll

using System;
using System.Collections.Generic;
using System.Linq;
using TaleWorlds.Core;
using TaleWorlds.DotNet;
using TaleWorlds.Engine;
using TaleWorlds.InputSystem;
using TaleWorlds.Library;
using TaleWorlds.MountAndBlade.Missions.Handlers;
using TaleWorlds.MountAndBlade.View.Screens;

#nullable disable
namespace TaleWorlds.MountAndBlade.View.MissionViews.Order
{
  public class OrderFlag
  {
    private readonly GameEntity _entity;
    private readonly GameEntity _flag;
    private readonly GameEntity _gear;
    private readonly GameEntity _arrow;
    private readonly GameEntity _width;
    private readonly GameEntity _attack;
    private readonly GameEntity _flagUnavailable;
    private readonly GameEntity _widthLeft;
    private readonly GameEntity _widthRight;
    public bool IsTroop = true;
    private bool _isWidthVisible;
    private float _customWidth;
    private GameEntity _current;
    private readonly IEnumerable<IOrderableWithInteractionArea> _orderablesWithInteractionArea;
    private readonly Mission _mission;
    private readonly MissionScreen _missionScreen;
    private readonly float _arrowLength;
    private bool _isArrowVisible;
    private Vec2 _arrowDirection;

    private GameEntity Current
    {
      get => this._current;
      set
      {
        if (!((NativeObject) this._current != (NativeObject) value))
          return;
        this._current = value;
        this._flag.SetVisibilityExcludeParents(false);
        this._gear.SetVisibilityExcludeParents(false);
        this._arrow.SetVisibilityExcludeParents(false);
        this._width.SetVisibilityExcludeParents(false);
        this._attack.SetVisibilityExcludeParents(false);
        this._flagUnavailable.SetVisibilityExcludeParents(false);
        this._current.SetVisibilityExcludeParents(true);
        if (!((NativeObject) this._current == (NativeObject) this._arrow) && !((NativeObject) this._current == (NativeObject) this._flagUnavailable))
          return;
        this._flag.SetVisibilityExcludeParents(true);
      }
    }

    public IOrderable FocusedOrderableObject { get; private set; }

    public int LatestUpdateFrameNo { get; private set; }

    public OrderFlag(Mission mission, MissionScreen missionScreen)
    {
      this._mission = mission;
      this._missionScreen = missionScreen;
      this._entity = GameEntity.CreateEmpty(this._mission.Scene);
      this._flag = GameEntity.CreateEmpty(this._mission.Scene);
      this._gear = GameEntity.CreateEmpty(this._mission.Scene);
      this._arrow = GameEntity.CreateEmpty(this._mission.Scene);
      this._width = GameEntity.CreateEmpty(this._mission.Scene);
      this._attack = GameEntity.CreateEmpty(this._mission.Scene);
      this._flagUnavailable = GameEntity.CreateEmpty(this._mission.Scene);
      this._widthLeft = GameEntity.CreateEmpty(this._mission.Scene);
      this._widthRight = GameEntity.CreateEmpty(this._mission.Scene);
      this._entity.EntityFlags |= EntityFlags.NotAffectedBySeason;
      this._flag.EntityFlags |= EntityFlags.NotAffectedBySeason;
      this._gear.EntityFlags |= EntityFlags.NotAffectedBySeason;
      this._arrow.EntityFlags |= EntityFlags.NotAffectedBySeason;
      this._width.EntityFlags |= EntityFlags.NotAffectedBySeason;
      this._attack.EntityFlags |= EntityFlags.NotAffectedBySeason;
      this._flagUnavailable.EntityFlags |= EntityFlags.NotAffectedBySeason;
      this._widthLeft.EntityFlags |= EntityFlags.NotAffectedBySeason;
      this._widthRight.EntityFlags |= EntityFlags.NotAffectedBySeason;
      this._flag.AddComponent((GameEntityComponent) MetaMesh.GetCopy("order_flag_a"));
      MatrixFrame frame1 = this._flag.GetFrame();
      frame1.Scale(new Vec3(10f, 10f, 10f));
      this._flag.SetFrame(ref frame1);
      this._gear.AddComponent((GameEntityComponent) MetaMesh.GetCopy("order_gear"));
      MatrixFrame frame2 = this._gear.GetFrame();
      frame2.Scale(new Vec3(10f, 10f, 10f));
      this._gear.SetFrame(ref frame2);
      this._arrow.AddComponent((GameEntityComponent) MetaMesh.GetCopy("order_arrow_a"));
      this._widthLeft.AddComponent((GameEntityComponent) MetaMesh.GetCopy("order_arrow_a"));
      this._widthRight.AddComponent((GameEntityComponent) MetaMesh.GetCopy("order_arrow_a"));
      MatrixFrame identity1 = MatrixFrame.Identity;
      identity1.rotation.RotateAboutUp(-1.57079637f);
      this._widthLeft.SetFrame(ref identity1);
      MatrixFrame identity2 = MatrixFrame.Identity;
      identity2.rotation.RotateAboutUp(1.57079637f);
      this._widthRight.SetFrame(ref identity2);
      this._width.AddChild(this._widthLeft);
      this._width.AddChild(this._widthRight);
      MetaMesh copy = MetaMesh.GetCopy("destroy_icon");
      copy.RecomputeBoundingBox(true);
      MatrixFrame frame3 = copy.Frame;
      frame3.Scale(new Vec3(0.15f, 0.15f, 0.15f));
      frame3.Elevate(10f);
      copy.Frame = frame3;
      this._attack.AddMultiMesh(copy);
      this._flagUnavailable.AddComponent((GameEntityComponent) MetaMesh.GetCopy("order_unavailable"));
      this._entity.AddChild(this._flag);
      this._entity.AddChild(this._gear);
      this._entity.AddChild(this._arrow);
      this._entity.AddChild(this._width);
      this._entity.AddChild(this._attack);
      this._entity.AddChild(this._flagUnavailable);
      this._flag.SetVisibilityExcludeParents(false);
      this._gear.SetVisibilityExcludeParents(false);
      this._arrow.SetVisibilityExcludeParents(false);
      this._width.SetVisibilityExcludeParents(false);
      this._attack.SetVisibilityExcludeParents(false);
      this._flagUnavailable.SetVisibilityExcludeParents(false);
      this.Current = this._flag;
      BoundingBox boundingBox = this._arrow.GetMetaMesh(0).GetBoundingBox();
      this._arrowLength = boundingBox.max.y - boundingBox.min.y;
      this.UpdateFrame(out bool _, false);
      this._orderablesWithInteractionArea = this._mission.MissionObjects.OfType<IOrderableWithInteractionArea>();
    }

    public void Tick(float dt)
    {
      this.FocusedOrderableObject = (IOrderable) null;
      GameEntity collidedEntity = this.GetCollidedEntity();
      if ((NativeObject) collidedEntity != (NativeObject) null)
      {
        BattleSideEnum side = Mission.Current.PlayerTeam.Side;
        IOrderable orderable1 = (IOrderable) collidedEntity.GetScriptComponents().First<ScriptComponentBehavior>((Func<ScriptComponentBehavior, bool>) (sc => sc is IOrderable orderable2 && orderable2.GetOrder(side) != 0));
        if (orderable1.GetOrder(side) != OrderType.None)
          this.FocusedOrderableObject = orderable1;
      }
      bool isOnValidGround;
      this.UpdateFrame(out isOnValidGround, (NativeObject) collidedEntity != (NativeObject) null);
      this.LatestUpdateFrameNo = Utilities.EngineFrameNo;
      if (!this.IsVisible)
        return;
      if (this.FocusedOrderableObject == null)
      {
        this.FocusedOrderableObject = (IOrderable) this._orderablesWithInteractionArea.FirstOrDefault<IOrderableWithInteractionArea>((Func<IOrderableWithInteractionArea, bool>) (o => ((ScriptComponentBehavior) o).GameEntity.IsVisibleIncludeParents() && o.IsPointInsideInteractionArea(this.Position)));
        if (this.FocusedOrderableObject is ScriptComponentBehavior focusedOrderableObject && (NativeObject) focusedOrderableObject.GameEntity.Scene == (NativeObject) null)
          this.FocusedOrderableObject = (IOrderable) null;
      }
      this.UpdateCurrentMesh(isOnValidGround);
      if (!((NativeObject) this.Current == (NativeObject) this._flag) && !((NativeObject) this.Current == (NativeObject) this._flagUnavailable))
        return;
      MatrixFrame frame = this._flag.GetFrame();
      float num = (MathF.Sin(MBCommon.GetApplicationTime() * 2f) + 1f) * 0.25f;
      frame.origin.z = num;
      this._flag.SetFrame(ref frame);
      this._flagUnavailable.SetFrame(ref frame);
    }

    private void UpdateCurrentMesh(bool isOnValidGround)
    {
      if (this.FocusedOrderableObject != null)
      {
        BattleSideEnum side = Mission.Current.PlayerTeam.Side;
        if (this.FocusedOrderableObject.GetOrder(side) == OrderType.AttackEntity)
        {
          this.Current = this._attack;
          return;
        }
        switch (this.FocusedOrderableObject.GetOrder(side))
        {
          case OrderType.FollowEntity:
          case OrderType.Use:
            this.Current = this._gear;
            return;
        }
      }
      if (this._isArrowVisible)
        this.Current = this._arrow;
      else if (this._isWidthVisible)
        this.Current = this._width;
      else
        this.Current = isOnValidGround ? this._flag : this._flagUnavailable;
    }

    public void SetArrowVisibility(bool isVisible, Vec2 arrowDirection)
    {
      this._isArrowVisible = isVisible;
      this._arrowDirection = arrowDirection;
    }

    private void UpdateFrame(out bool isOnValidGround, bool checkForTargetEntity)
    {
      Vec3 groundPosition;
      Vec3 vec3;
      if (this._missionScreen.GetProjectedMousePositionOnGround(out groundPosition, out vec3, BodyFlags.BodyOwnerFlora, true))
      {
        WorldPosition orderPosition = new WorldPosition(Mission.Current.Scene, UIntPtr.Zero, groundPosition, false);
        isOnValidGround = !this.IsVisible | checkForTargetEntity ? Mission.Current.IsOrderPositionAvailable(in orderPosition, Mission.Current.PlayerTeam) : OrderFlag.IsPositionOnValidGround(orderPosition);
      }
      else
      {
        isOnValidGround = false;
        groundPosition = new Vec3(z: -100000f);
      }
      this.Position = groundPosition;
      Vec3 rayBegin;
      this._missionScreen.ScreenPointToWorldRay(Vec2.One * 0.5f, out rayBegin, out vec3);
      float a = this._missionScreen.LastFollowedAgent == null ? this._missionScreen.CombatCamera.Frame.rotation.f.RotationZ : (rayBegin - this.Position).AsVec2.RotationInRadians;
      MatrixFrame frame = this._entity.GetFrame() with
      {
        rotation = Mat3.Identity
      };
      frame.rotation.RotateAboutUp(a);
      this._entity.SetFrame(ref frame);
      if (this._isArrowVisible)
      {
        float rotationInRadians = this._arrowDirection.RotationInRadians;
        Mat3 identity1 = Mat3.Identity;
        identity1.RotateAboutUp(rotationInRadians);
        MatrixFrame identity2 = MatrixFrame.Identity with
        {
          rotation = frame.rotation.TransformToLocal(identity1)
        };
        identity2.Advance(-this._arrowLength);
        this._arrow.SetFrame(ref identity2);
      }
      if (!this._isWidthVisible)
        return;
      this._widthLeft.SetLocalPosition(Vec3.Side * (float) ((double) this._customWidth * 0.5 - 0.0));
      this._widthRight.SetLocalPosition(Vec3.Side * (float) ((double) this._customWidth * -0.5 + 0.0));
      this._widthLeft.SetLocalPosition(Vec3.Side * (this._customWidth * 0.5f - this._arrowLength));
      this._widthRight.SetLocalPosition(Vec3.Side * (this._customWidth * -0.5f + this._arrowLength));
    }

    public static bool IsPositionOnValidGround(WorldPosition worldPosition)
    {
      return Mission.Current.IsFormationUnitPositionAvailable(ref worldPosition, Mission.Current.PlayerTeam);
    }

    public static bool IsOrderPositionValid(WorldPosition orderPosition)
    {
      return Mission.Current.IsOrderPositionAvailable(in orderPosition, Mission.Current.PlayerTeam);
    }

    public Vec3 Position
    {
      get => this._entity.GlobalPosition;
      private set
      {
        MatrixFrame frame = this._entity.GetFrame() with
        {
          origin = value
        };
        this._entity.SetFrame(ref frame);
      }
    }

    public MatrixFrame Frame => this._entity.GetGlobalFrame();

    public bool IsVisible
    {
      get => this._entity.IsVisibleIncludeParents();
      set
      {
        this._entity.SetVisibilityExcludeParents(value);
        if (value)
          return;
        this.FocusedOrderableObject = (IOrderable) null;
      }
    }

    private GameEntity GetCollidedEntity()
    {
      Vec3 rayBegin;
      Vec3 rayEnd;
      this._missionScreen.ScreenPointToWorldRay(Mission.Current.GetMissionBehavior<BattleDeploymentHandler>() != null ? Input.MousePositionRanged : new Vec2(0.5f, 0.5f), out rayBegin, out rayEnd);
      GameEntity collidedEntity;
      this._mission.Scene.RayCastForClosestEntityOrTerrain(rayBegin, rayEnd, out float _, out collidedEntity, 0.3f, BodyFlags.CommonFocusRayCastExcludeFlags | BodyFlags.BodyOwnerFlora);
      while ((NativeObject) collidedEntity != (NativeObject) null && !collidedEntity.GetScriptComponents().Any<ScriptComponentBehavior>((Func<ScriptComponentBehavior, bool>) (sc => sc is IOrderable orderable && orderable.GetOrder(Mission.Current.PlayerTeam.Side) != 0)))
        collidedEntity = collidedEntity.Parent;
      return collidedEntity;
    }

    public void SetWidthVisibility(bool isVisible, float width)
    {
      this._isWidthVisible = isVisible;
      this._customWidth = width;
    }
  }
}
